Skip to contentMethod: getEntityFromCacheOrOntology(Class, URI, Descriptor)
1: /**
2: * Copyright (C) 2016 Czech Technical University in Prague
3: *
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: *
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.jopa.oom;
16:
17: import cz.cvut.kbss.jopa.exceptions.StorageAccessException;
18: import cz.cvut.kbss.jopa.model.MetamodelImpl;
19: import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
20: import cz.cvut.kbss.jopa.model.descriptors.EntityDescriptor;
21: import cz.cvut.kbss.jopa.model.metamodel.EntityType;
22: import cz.cvut.kbss.jopa.model.metamodel.EntityTypeImpl;
23: import cz.cvut.kbss.jopa.oom.exceptions.EntityDeconstructionException;
24: import cz.cvut.kbss.jopa.oom.exceptions.EntityReconstructionException;
25: import cz.cvut.kbss.jopa.oom.exceptions.UnpersistedChangeException;
26: import cz.cvut.kbss.jopa.sessions.CacheManager;
27: import cz.cvut.kbss.jopa.sessions.LoadingParameters;
28: import cz.cvut.kbss.jopa.sessions.UnitOfWorkImpl;
29: import cz.cvut.kbss.jopa.utils.Configuration;
30: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
31: import cz.cvut.kbss.ontodriver.Connection;
32: import cz.cvut.kbss.ontodriver.descriptor.AxiomDescriptor;
33: import cz.cvut.kbss.ontodriver.descriptor.ReferencedListDescriptor;
34: import cz.cvut.kbss.ontodriver.descriptor.SimpleListDescriptor;
35: import cz.cvut.kbss.ontodriver.exception.OntoDriverException;
36: import cz.cvut.kbss.ontodriver.model.*;
37: import org.slf4j.Logger;
38: import org.slf4j.LoggerFactory;
39:
40: import java.lang.reflect.Field;
41: import java.net.URI;
42: import java.util.Collection;
43: import java.util.Map;
44: import java.util.Map.Entry;
45: import java.util.Objects;
46:
47: public class ObjectOntologyMapperImpl implements ObjectOntologyMapper, EntityMappingHelper {
48:
49: private static final Logger LOG = LoggerFactory.getLogger(ObjectOntologyMapperImpl.class);
50:
51: private final UnitOfWorkImpl uow;
52: private final CacheManager cache;
53: private final Connection storageConnection;
54: private final MetamodelImpl metamodel;
55:
56: private final AxiomDescriptorFactory descriptorFactory;
57: private final EntityConstructor entityBuilder;
58: private final EntityDeconstructor entityBreaker;
59: private final InstanceRegistry instanceRegistry;
60: private final PendingChangeRegistry pendingPersists;
61:
62: private final EntityInstanceLoader defaultInstanceLoader;
63: private final EntityInstanceLoader twoStepInstanceLoader;
64:
65: public ObjectOntologyMapperImpl(UnitOfWorkImpl uow, Connection connection) {
66: this.uow = Objects.requireNonNull(uow);
67: this.cache = uow.getLiveObjectCache();
68: this.storageConnection = Objects.requireNonNull(connection);
69: this.metamodel = uow.getMetamodel();
70: this.descriptorFactory = new AxiomDescriptorFactory();
71: this.instanceRegistry = new InstanceRegistry();
72: this.pendingPersists = new PendingChangeRegistry();
73: this.entityBuilder = new EntityConstructor(this);
74: this.entityBreaker = new EntityDeconstructor(this);
75:
76: this.defaultInstanceLoader = DefaultInstanceLoader.builder().connection(storageConnection).metamodel(metamodel)
77: .descriptorFactory(descriptorFactory)
78: .entityBuilder(entityBuilder).cache(cache).build();
79: this.twoStepInstanceLoader = TwoStepInstanceLoader.builder().connection(storageConnection).metamodel(metamodel)
80: .descriptorFactory(descriptorFactory)
81: .entityBuilder(entityBuilder).cache(cache).build();
82: }
83:
84: @Override
85: public <T> boolean containsEntity(Class<T> cls, URI primaryKey, Descriptor descriptor) {
86: assert cls != null;
87: assert primaryKey != null;
88: assert descriptor != null;
89:
90: final EntityType<T> et = getEntityType(cls);
91: final NamedResource classUri = NamedResource.create(et.getIRI().toURI());
92: final Axiom<NamedResource> ax = new AxiomImpl<>(NamedResource.create(primaryKey),
93: Assertion.createClassAssertion(false), new Value<>(classUri));
94: try {
95: return storageConnection.contains(ax, descriptor.getContext());
96: } catch (OntoDriverException e) {
97: throw new StorageAccessException(e);
98: }
99: }
100:
101: @Override
102: public <T> T loadEntity(LoadingParameters<T> loadingParameters) {
103: assert loadingParameters != null;
104:
105: instanceRegistry.reset();
106: return loadEntityInternal(loadingParameters);
107: }
108:
109: private <T> T loadEntityInternal(LoadingParameters<T> loadingParameters) {
110: final EntityTypeImpl<T> et = getEntityType(loadingParameters.getEntityType());
111: if (et.hasSubtypes()) {
112: return twoStepInstanceLoader.loadEntity(loadingParameters);
113: }
114: return defaultInstanceLoader.loadEntity(loadingParameters);
115: }
116:
117: @Override
118: public <T> EntityTypeImpl<T> getEntityType(Class<T> cls) {
119: return metamodel.entity(cls);
120: }
121:
122: @Override
123: public <T> void loadFieldValue(T entity, Field field, Descriptor descriptor) {
124: assert entity != null;
125: assert field != null;
126: assert descriptor != null;
127:
128: LOG.trace("Lazily loading value of field {} of entity {}.", field, entity);
129:
130: final EntityType<T> et = (EntityType<T>) getEntityType(entity.getClass());
131: final URI primaryKey = EntityPropertiesUtils.getPrimaryKey(entity, et);
132:
133: final AxiomDescriptor axiomDescriptor = descriptorFactory.createForFieldLoading(primaryKey,
134: field, descriptor, et);
135: try {
136: final Collection<Axiom<?>> axioms = storageConnection.find(axiomDescriptor);
137: if (axioms.isEmpty()) {
138: return;
139: }
140: entityBuilder.setFieldValue(entity, field, axioms, et, descriptor);
141: } catch (OntoDriverException e) {
142: throw new StorageAccessException(e);
143: } catch (IllegalArgumentException | IllegalAccessException e) {
144: throw new EntityReconstructionException(e);
145: }
146: }
147:
148: @Override
149: public <T> void persistEntity(URI primaryKey, T entity, Descriptor descriptor) {
150: assert entity != null;
151: assert descriptor != null;
152:
153: @SuppressWarnings("unchecked")
154: final EntityType<T> et = (EntityType<T>) getEntityType(entity.getClass());
155: try {
156: if (primaryKey == null) {
157: primaryKey = generateIdentifier(et);
158: assert primaryKey != null;
159: EntityPropertiesUtils.setPrimaryKey(primaryKey, entity, et);
160: }
161: entityBreaker.setCascadeResolver(new PersistCascadeResolver(this));
162: final AxiomValueGatherer axiomBuilder = entityBreaker.mapEntityToAxioms(primaryKey,
163: entity, et, descriptor);
164: axiomBuilder.persist(storageConnection);
165: pendingPersists.removeInstance(primaryKey, descriptor.getContext());
166: } catch (IllegalArgumentException e) {
167: throw new EntityDeconstructionException("Unable to deconstruct entity " + entity, e);
168: }
169: }
170:
171: @Override
172: public URI generateIdentifier(EntityType<?> et) {
173: try {
174: return storageConnection.generateIdentifier(et.getIRI().toURI());
175: } catch (OntoDriverException e) {
176: throw new StorageAccessException(e);
177: }
178: }
179:
180: @Override
181: public <T> T getEntityFromCacheOrOntology(Class<T> cls, URI primaryKey, Descriptor descriptor) {
182: final T orig = uow.getManagedOriginal(cls, primaryKey, descriptor);
183:• if (orig != null) {
184: return orig;
185: }
186:• if (cache.contains(cls, primaryKey, descriptor.getContext())) {
187: return cache.get(cls, primaryKey, descriptor.getContext());
188:• } else if (instanceRegistry.containsInstance(primaryKey, descriptor.getContext())) {
189: // This prevents endless cycles in bidirectional relationships
190: return cls.cast(instanceRegistry.getInstance(primaryKey, descriptor.getContext()));
191: } else {
192: return loadEntityInternal(new LoadingParameters<>(cls, primaryKey, descriptor));
193: }
194: }
195:
196: @Override
197: public <T> T getOriginalInstance(T clone) {
198: assert clone != null;
199: return (T) uow.getOriginal(clone);
200: }
201:
202: <T> void registerInstance(URI primaryKey, T instance, URI context) {
203: instanceRegistry.registerInstance(primaryKey, instance, context);
204: }
205:
206: @Override
207: public void checkForUnpersistedChanges() {
208: try {
209: final Map<URI, Map<URI, Object>> persists = pendingPersists.getInstances();
210: if (!persists.isEmpty()) {
211: for (URI ctx : persists.keySet()) {
212: for (Entry<URI, Object> e : persists.get(ctx).entrySet()) {
213: verifyInstanceExistInOntology(ctx, e.getKey(), e.getValue());
214: }
215: }
216: }
217: } catch (OntoDriverException e) {
218: throw new StorageAccessException(e);
219: }
220: }
221:
222: private void verifyInstanceExistInOntology(URI ctx, URI primaryKey, Object instance)
223: throws OntoDriverException {
224: boolean exists = containsEntity(instance.getClass(), primaryKey, new EntityDescriptor(ctx));
225: if (!exists) {
226: throw new UnpersistedChangeException(
227: "Encountered an instance that was neither persisted nor marked as cascade for persist. The instance: "
228: + instance);
229: }
230: }
231:
232: <T> void registerPendingPersist(URI primaryKey, T entity, URI context) {
233: pendingPersists.registerInstance(primaryKey, entity, context);
234: }
235:
236: @Override
237: public <T> void removeEntity(URI primaryKey, Class<T> cls, Descriptor descriptor) {
238: final EntityType<T> et = getEntityType(cls);
239: final AxiomDescriptor axiomDescriptor = descriptorFactory.createForEntityLoading(
240: new LoadingParameters<>(cls, primaryKey, descriptor, true), et);
241: try {
242: storageConnection.remove(axiomDescriptor);
243: } catch (OntoDriverException e) {
244: throw new StorageAccessException("Exception caught when removing entity.", e);
245: }
246: }
247:
248: @Override
249: public <T> void updateFieldValue(T entity, Field field, Descriptor descriptor) {
250: @SuppressWarnings("unchecked")
251: final EntityType<T> et = (EntityType<T>) getEntityType(entity.getClass());
252: final URI pkUri = EntityPropertiesUtils.getPrimaryKey(entity, et);
253:
254: entityBreaker.setCascadeResolver(new PersistCascadeResolver(this));
255: final AxiomValueGatherer axiomBuilder = entityBreaker.mapFieldToAxioms(pkUri, entity, field,
256: et, descriptor);
257: axiomBuilder.update(storageConnection);
258: }
259:
260: @Override
261: public Collection<Axiom<NamedResource>> loadSimpleList(SimpleListDescriptor listDescriptor) {
262: try {
263: return storageConnection.lists().loadSimpleList(listDescriptor);
264: } catch (OntoDriverException e) {
265: throw new StorageAccessException(e);
266: }
267: }
268:
269: @Override
270: public Collection<Axiom<NamedResource>> loadReferencedList(ReferencedListDescriptor listDescriptor) {
271: try {
272: return storageConnection.lists().loadReferencedList(listDescriptor);
273: } catch (OntoDriverException e) {
274: throw new StorageAccessException(e);
275: }
276: }
277:
278: @Override
279: public Configuration getConfiguration() {
280: return uow.getConfiguration();
281: }
282: }